package HTTPServer;

import java.net.*;
import java.util.*;
import java.io.*;

public class ConcreteHTTPServer implements HTTPServerStrategy {

    int port = 0;
    String wwwhome = null;
    Socket connection;
    BufferedReader in;
    OutputStream out;
    PrintStream pOut;
    
    public ConcreteHTTPServer(int port, String wwwhome) {
        this.port = port;
        this.wwwhome = wwwhome;
    }

    public void start() {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(port);
        } catch (IOException ex) {
            System.err.println("Could not start server: " + ex);
            System.exit(-1);
        }

        System.out.println("WebServer accepting connections on port " + port);

        while (true) {
            try {
                connection = serverSocket.accept();
                in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                out = new BufferedOutputStream(connection.getOutputStream());
                pOut = new PrintStream(out);

                String request = in.readLine();
                connection.shutdownInput();
                log(connection, request);

                processRequest(request);

                pOut.flush();
            } catch (IOException ex) {
                System.err.println(ex);
            }

            try {
                if (connection != null) {
                    connection.close();
                }
            } catch (IOException ex) {
                System.err.println(ex);
            }
        }
    }

    public void stop() {

    }

    public void processRequest(String request) throws IOException {
        
        if (!(request.startsWith("GET") || request.startsWith("POST") || request.startsWith("HEAD"))
                || request.length() < 14 
                || !(request.endsWith("HTTP/1.0") || request.endsWith("HTTP/1.1"))) {
            errorReport(pOut, connection, "400", "Bad Request",
                    "Your browser sent a request that " +
                    "this server could not understand.");
        } else {
            MethodStrategy method = null;
            
            if (request.startsWith("GET")) {
                method = new GetMethod(this);
            } else if (request.startsWith("POST")) {
                method = new PostMethod(this);
            } else if (request.startsWith("HEAD")) {
                //method = new HeadMethod(this);
            }
            if (method!=null) method.processRequest(request);
        }
        System.out.println(request);
    }

    public void errorReport(PrintStream pOut, Socket con, String code, String title, String msg) {
        pOut.print(
                "HTTP/1.0 " + code + " " + title + "\r\n" +
                "\r\n" +
                "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" +
                "<TITLE>" + code + " " + title + "</TITLE>\r\n" +
                "</HEAD><BODY>\r\n" +
                "<H1>" + title + "</H1>\r\n" + msg + "<P>\r\n" +
                "<HR><ADDRESS>DENIAL WebServer 1.0 at " +
                con.getLocalAddress().getHostName() +
                " Port " + con.getLocalPort() + "</ADDRESS>\r\n" +
                "</BODY></HTML>\r\n");
        log(con, code + " " + title);
    }

    public void log(Socket connection, String msg) {
        System.err.println(new Date() + " [" + connection.getInetAddress().getHostAddress() + ":" + connection.getPort() + "] " + msg);
    }

    public void sendFile(InputStream file, OutputStream out) throws IOException {
        byte[] buffer = new byte[1000];
        while (file.available() > 0) {
            out.write(buffer, 0, file.read(buffer));
        }
    }
    
        public int getPort() {
        return this.port;
    }
    
    public String getWwwhome() {
        return this.wwwhome;
    }
    
    public Socket getConnection() {
        return this.connection;
    }
    
    public BufferedReader getBufferedReader() {
        return this.in;
    }
    
    public OutputStream getOut() {
        return this.out;
    }
    
    public PrintStream getPOut() {
        return this.pOut;
    }
}
